package com.winsdom.service.impl;

import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.winsdom.context.BaseContext;
import com.winsdom.entity.PaymentReceipt;
import com.winsdom.entity.SysUser;
import com.winsdom.exception.BusinessException;
import com.winsdom.mapper.*;
import com.winsdom.service.ICustomerBillingHeaderService;
import com.winsdom.service.PaymentService;
import com.winsdom.utils.PayUtil;
import com.winsdom.vo.CustomerBillingConfigQueryVO;
import com.winsdom.vo.CustomerBillingHeaderQueryVO;
import com.winsdom.vo.uniformCharge.*;
import com.winsdom.service.UnifiedChargingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 统一收费服务实现类
 */
@Service
@Slf4j
public class UnifiedChargingServiceImpl implements UnifiedChargingService {

    @Autowired
    private UnifiedChargingMapper unifiedChargingMapper;
    @Autowired
    private PayUtil payUtil;
    @Autowired
    private PaymentReceiptMapper paymentReceiptMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private CustomerBillingConfigMapper customerBillingConfigMapper;
    @Autowired
    private ICustomerBillingHeaderService iCustomerBillingHeaderService;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private PrestoreRecordMapper prestoreRecordMapper;
    /**
     * 分页查询客户待缴与预存余额金额信息
     *
     * @param categoryId 收费类别ID
     * @param pageNum    页码
     * @param pageSize   每页大小
     * @return 分页结果
     */
    @Override
    public Page<ChargingInformationVO> search(int pageNum, int pageSize, int categoryId) {
        try {
            Page<ChargingInformationVO> result = unifiedChargingMapper.selectChargingInfoByCategoryId(new Page<>(pageNum, pageSize), categoryId);
            log.info("成功查询收费信息, 总记录数: {}", result.getTotal());
            return result;
        } catch (Exception e) {
            log.error("查询收费信息失败, categoryId: {}, 错误信息: {}", categoryId, e.getMessage(), e);
            throw new BusinessException("查询信息失败", HttpStatus.BAD_REQUEST);
        }
    }

    /**
     * 分页查询缴费历史信息
     *
     * @param pageNum       页码
     * @param pageSize      每页大小
     * @param categoryId    资源类别ID
     * @param customerName  客户名称（可选）
     * @param invoiceNumber 发票号码（可选）
     * @return 分页结果
     */
    @Override
    public Page<PaymentHistoryVO> searchHistory(int pageNum, int pageSize, int categoryId, String customerName, String invoiceNumber) {
        try {
            Page<PaymentHistoryVO> result = unifiedChargingMapper.selectChargeHistory(new Page<>(pageNum, pageSize), categoryId, customerName, invoiceNumber);
            log.info("成功查询缴费历史, 总记录数: {}", result.getTotal());
            return result;
        } catch (Exception e) {
            log.error("查询缴费历史失败, categoryId: {}, 错误信息: {}", categoryId, e.getMessage(), e);
            throw new BusinessException("查询缴费历史失败", HttpStatus.BAD_REQUEST);
        }
    }

    /**
     * 应收详情
     * @return
     */
    @Override
    public List<BillingResponseVO> receive(Integer customerId) {

        List<BillingVO> billingVOList = unifiedChargingMapper.receive(customerId);

        // 按账期分组
        Map<String, List<BillingVO>> groupedByBillingPeriod = billingVOList.stream()
                .collect(Collectors.groupingBy(BillingVO::getBillingPeriod));

        List<BillingResponseVO> responseVOList = new ArrayList<>();

        for (Map.Entry<String, List<BillingVO>> entry : groupedByBillingPeriod.entrySet()) {
            String billingPeriod = entry.getKey();
            List<BillingVO> billings = entry.getValue();

            // 按账单编号分组
            Map<String, List<BillingVO>> groupedByBillNumber = billings.stream()
                    .collect(Collectors.groupingBy(BillingVO::getBillNumber));

            for (Map.Entry<String, List<BillingVO>> billEntry : groupedByBillNumber.entrySet()) {
                String billNumber = billEntry.getKey();
                List<BillingVO> resources = billEntry.getValue();

                BillingResponseVO responseVO = new BillingResponseVO();
                responseVO.setBillingPeriod(billingPeriod);
                responseVO.setBillNumber(billNumber);
                responseVO.setResources(resources);

                responseVOList.add(responseVO);
            }
        }
        // 按 billingPeriod 升序排序
        responseVOList.sort((o1, o2) -> o1.getBillingPeriod().compareTo(o2.getBillingPeriod()));

        return responseVOList;

    }

    /**
     * 查询选中的计费明细
     * @param detailIds
     * @return
     */
    @Override
    public List<BillingVO> getBillingDetailsByIds(List<Integer> detailIds) {
        return unifiedChargingMapper.getBillingDetailsByIds(detailIds);
    }

    /**
     * 开始收费
     * @param detailIds
     * @return
     */
    @Override
    public StartChargeVO startCharge(List<Integer> detailIds,Integer customerId) {
        // 使用 receive 方法获取客户所有应收详情（包含无对应账单的明细）
        List<BillingResponseVO> billingResponseList = receive(customerId);
        // 提取所有明细 ID 用于验证
        Set<Integer> validDetailIds = new HashSet<>();
        for (BillingResponseVO response : billingResponseList) {
            for (BillingVO resource : response.getResources()) {
                validDetailIds.add(resource.getDetailId());
            }
        }

        // 验证选中的明细是否都属于该客户
        boolean allValid = detailIds.stream().allMatch(validDetailIds::contains);
        if (!allValid) {
            throw new BusinessException("选中的收费明细包含非当前客户的记录或无效记录", HttpStatus.BAD_REQUEST);
        }

        // 查询选中的收费明细
        List<BillingVO> billingVOList = unifiedChargingMapper.getBillingDetailsByIds(detailIds);
        if (billingVOList.isEmpty()) {
            throw new BusinessException("未找到选中的收费明细", HttpStatus.BAD_REQUEST);
        }

        BigDecimal totalAmount = billingVOList.stream()
                .map(BillingVO::getAmountSubtotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 获取客户开票配置（复用封装方法）
        CustomerBillingConfigQueryVO billingConfig = getCustomerBillingConfig(Long.valueOf(customerId));
        String billingMethod = billingConfig.getBillingMethod();

        //收款人为当前登录的用户的名字
        Integer userId = BaseContext.getCurrentId();
        String name = sysUserMapper.selectOne(
                new LambdaQueryWrapper<SysUser>().eq(SysUser::getId, userId)
        ).getName();

        //先款后票
        if ("先款后票".equals(billingMethod)) {
            // 开票区域可见，需选择开票类型
            Integer invoiceType = convertBillingTypeToInvoiceType(billingConfig.getBillingType());

            //根据客户ID查客户发票抬头list
            List<CustomerBillingHeaderQueryVO> headersByCustomerId = iCustomerBillingHeaderService.getHeadersByCustomerId(Long.valueOf(customerId));
            log.info("查到的客户发票抬头信息:{}", headersByCustomerId);

            //如果开票类型为纸质收据时，不需选择发票抬头
            if (invoiceType == 5) {
                headersByCustomerId = null;
            }

            //TODO 滞纳金、客户开票配置（先款后票、先票后款）
            return StartChargeVO.builder()
                    .billingVOList(billingVOList)
                    .invoiceType(invoiceType)
                    .customerBillingHeaderQueryVOList(headersByCustomerId)
                    .payee(name)
                    .payDate(LocalDate.now())
                    .totalAmount(totalAmount)
                    .build();
        }
        //如果是先票后款 不需要返回开票的信息
        return StartChargeVO.builder()
                .billingVOList(billingVOList)
                .payee(name)
                .payDate(LocalDate.now())
                .totalAmount(totalAmount)
                .build();

    }

    /**
     * 确认收费
     */
    @Override
    @Transactional
    public ChargeResponseVO confirmPayment(ChargeRequestDTO chargeRequest) {
        List<Integer> detailIds = chargeRequest.getDetailIds();
        Long customerId = chargeRequest.getCustomerId();
        Integer paymentMethod = chargeRequest.getPaymentMethod();
        String chequeNumber = chargeRequest.getChequeNumber();
        log.info("开始处理支付请求, 客户ID: {}, 支付方式: {}", customerId, paymentMethod);
        Long headerId = chargeRequest.getHeaderId();
        Integer invoiceType = chargeRequest.getInvoiceType();

        // 获取并校验客户开票配置
        CustomerBillingConfigQueryVO billingConfig = getCustomerBillingConfig(customerId);
        validateInvoiceParamsByBillingMethod(billingConfig, invoiceType, headerId);

        //非支票支付时，chequeNumber 必须为空
        if (paymentMethod != 2 && chequeNumber != null && !chequeNumber.isEmpty()) {
            throw new BusinessException("非支票支付不允许填写支票号码", HttpStatus.BAD_REQUEST);
        }

        // 检查发票抬头是否属于该客户（先款后票场景才需要校验）
        if ("先款后票".equals(billingConfig.getBillingMethod()) && headerId != null) {
            List<CustomerBillingHeaderQueryVO> headersByCustomerId = iCustomerBillingHeaderService.getHeadersByCustomerId(customerId);
            boolean isValidHeader = headersByCustomerId.stream()
                    .anyMatch(header -> header.getHeaderId().equals(headerId));
            if (!isValidHeader) {
                throw new BusinessException("所选发票抬头不属于该客户", HttpStatus.BAD_REQUEST);
            }
        }

        // 获取当前用户信息
        Integer userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new BusinessException("当前用户未登录", HttpStatus.UNAUTHORIZED);
        }
        String name = sysUserMapper.selectOne(
                new LambdaQueryWrapper<SysUser>().eq(SysUser::getId, userId)
        ).getName();

        // 查询选中的收费明细
        List<BillingVO> billingVOList = getBillingDetailsByIds(detailIds);
        if (billingVOList.isEmpty()) {
            throw new BusinessException("未找到选中的收费明细", HttpStatus.BAD_REQUEST);
        }

        // 计算总金额
        BigDecimal totalAmount = billingVOList.stream()
                .map(BillingVO::getAmountSubtotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 构建支付记录实体
        PaymentReceipt paymentReceipt = new PaymentReceipt();
        paymentReceipt.setCustomerId(customerId);
        paymentReceipt.setReceiptAmount(totalAmount);
        paymentReceipt.setStatus(0); // 初始状态为处理中
        paymentReceipt.setCreateTime(LocalDateTime.now());
        paymentReceipt.setPaymentMethod(paymentMethod);
        paymentReceipt.setPayeeName(name); // 设置收款人

        ChargeResponseVO chargeResponseVO = new ChargeResponseVO();
        String receiptSerial = null;

        // 按支付方式处理流水号和业务逻辑
        switch (paymentMethod) {
            case 1: // 现金支付
                receiptSerial = "CASH_" + System.currentTimeMillis() + "_" + ThreadLocalRandom.current().nextInt(10000);
                paymentReceipt.setReceiptSerial(receiptSerial);
                chargeResponseVO.setOutTradeNo(receiptSerial);

                paymentReceiptMapper.insert(paymentReceipt);
                handlePaymentSuccess(receiptSerial, chargeRequest);
                break;

            case 2: // 支票支付
                if (chequeNumber == null || chequeNumber.isEmpty()) {
                    throw new BusinessException("支票支付必须填写支票号码", HttpStatus.BAD_REQUEST);
                }
                // 检查支票号码唯一性
                long chequeCount = paymentReceiptMapper.selectCount(
                        new LambdaQueryWrapper<PaymentReceipt>().eq(PaymentReceipt::getReceiptSerial, chequeNumber)
                );
                if (chequeCount > 0) {
                    throw new BusinessException("支票号码已存在", HttpStatus.BAD_REQUEST);
                }
                paymentReceipt.setReceiptSerial(chequeNumber);
                chargeResponseVO.setOutTradeNo(chequeNumber);

                paymentReceiptMapper.insert(paymentReceipt);
                handlePaymentSuccess(chequeNumber, chargeRequest);
                break;

            case 3: // 微信支付（待扩展）
            case 4: // 支付宝支付
                receiptSerial = "REC_" + System.currentTimeMillis() + "_" + ThreadLocalRandom.current().nextInt(1000);
                paymentReceipt.setReceiptSerial(receiptSerial);
                chargeResponseVO.setOutTradeNo(receiptSerial);

                paymentReceiptMapper.insert(paymentReceipt);
                log.info("支付宝支付记录插入成功，流水号：{}", receiptSerial);
                // 生成收款码（仅第三方支付需要）
                if (paymentMethod == 4) {
                    try {
                        String qrCodeUrl = payUtil.sendRequestToAlipay(receiptSerial, totalAmount.floatValue(), "统一收费");
                        chargeResponseVO.setQrCodeUrl(qrCodeUrl);
                    } catch (AlipayApiException e) {
                        log.error("生成支付宝收款码失败: {}", e.getMessage(), e);
                        throw new BusinessException("生成收款码失败", HttpStatus.INTERNAL_SERVER_ERROR);
                    }
                }
                // 第三方支付无需立即处理，依赖回调接口触发handlePaymentSuccess
                break;

            default:
                throw new BusinessException("不支持的支付方式", HttpStatus.BAD_REQUEST);
        }

        // 设置响应结果
        chargeResponseVO.setTotalAmount(totalAmount);

        // 存储请求到Redis
        try {
            if (receiptSerial != null) {
                stringRedisTemplate.opsForValue().set(
                        receiptSerial,
                        objectMapper.writeValueAsString(chargeRequest),
                        3600,
                        TimeUnit.SECONDS
                );
            }
        } catch (JsonProcessingException e) {
            log.error("序列化支付请求失败: {}", e.getMessage(), e);
            throw new BusinessException("系统内部错误", HttpStatus.INTERNAL_SERVER_ERROR);
        }

        return chargeResponseVO;
    }

    /**
     * 内部调用支付成功处理逻辑
     */
    private void handlePaymentSuccess(String receiptSerial, ChargeRequestDTO chargeRequest) throws BusinessException {
        try {
            paymentService.handlePaymentSuccess(receiptSerial, chargeRequest);
        } catch (Exception e) {
            log.error("处理支付成功逻辑失败: {}", e.getMessage(), e);
            throw new BusinessException("支付处理失败", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 获取客户开票配置
     */
    private CustomerBillingConfigQueryVO getCustomerBillingConfig(Long customerId) {
        CustomerBillingConfigQueryVO billingConfig = customerBillingConfigMapper.selectBillingConfigByCustomerId(customerId);
        if (billingConfig == null) {
            throw new BusinessException("未找到客户开票配置", HttpStatus.BAD_REQUEST);
        }
        return billingConfig;
    }

    /**
     * 根据开票方式校验发票参数
     */
    private void validateInvoiceParamsByBillingMethod(CustomerBillingConfigQueryVO billingConfig,
                                                      Integer invoiceType,
                                                      Long headerId) {
        String billingMethod = billingConfig.getBillingMethod();

        if ("先款后票".equals(billingMethod)) {
            if (invoiceType == null) {
                throw new BusinessException("请选择发票类型", HttpStatus.BAD_REQUEST);
            }
            if (headerId == null) {
                throw new BusinessException("请选择发票抬头", HttpStatus.BAD_REQUEST);
            }
            if (invoiceType == 5 && headerId != null) { // 纸质收据不允许有抬头
                throw new BusinessException("纸质收据无需选择发票抬头", HttpStatus.BAD_REQUEST);
            }
        } else {
            // 先票后款：禁止所有开票参数
            if (invoiceType != null) {
                throw new BusinessException("先票后款客户不允许选择发票类型", HttpStatus.BAD_REQUEST);
            }
            if (headerId != null) {
                throw new BusinessException("先票后款客户不允许选择发票抬头", HttpStatus.BAD_REQUEST);
            }
        }
    }

    /**
     * 将数据库中的开票类型转换为系统内部的发票类型编码
     */
    private Integer convertBillingTypeToInvoiceType(String billingType) {
        if ("增值税电子普通发票".equals(billingType)) return 1;
        if ("增值税专用发票".equals(billingType)) return 2;
        if ("电子发票(增值税普票)".equals(billingType)) return 3;
        if ("电子发票(增值税专发票)".equals(billingType)) return 4;
        if ("纸质收据".equals(billingType)) return 5;
        return null;
    }


    // 获取当前登录的用户信息
    private SysUser getCurrentUser() {
        Integer userId = BaseContext.getCurrentId();
        SysUser user = sysUserMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("当前用户未登录", HttpStatus.UNAUTHORIZED);
        }
        return user;
    }

    // 获取客户的通用预存余额
    private BigDecimal getCustomerPrestoreBalanceByType(Integer customerId, String prestoreType) {
        // 实现逻辑：根据客户ID和预存类型查询预存余额
        // 这里需要根据你的数据库设计实现查询逻辑
        return BigDecimal.ZERO;
    }

    @Override
    public AllChargeItemsResponseVO getAllChargeItemsWithDetails() {
        List<ChargeItemVO> tempChargeItems = unifiedChargingMapper.getTempChargeItems();
        List<BondTypeWithItemsVO> bondTypesWithItems = unifiedChargingMapper.getBondTypesWithItems();
        List<PrestoreTypeWithItemsVO> prestoreTypesWithItems = unifiedChargingMapper.getPrestoreTypesWithItems();

        // 处理临时性计费项目
        // 这里可以直接使用 tempChargeItems，无需额外处理

        // 处理保证金类型及其收费项目
        Map<String, BondTypeWithItemsVO> bondTypeMap = new HashMap<>();
        for (BondTypeWithItemsVO item : bondTypesWithItems) {
            if (!bondTypeMap.containsKey(item.getBondTypeId())) {
                bondTypeMap.put(item.getBondTypeId(), new BondTypeWithItemsVO());
                bondTypeMap.get(item.getBondTypeId()).setBondTypeId(item.getBondTypeId());
                bondTypeMap.get(item.getBondTypeId()).setBondTypeName(item.getBondTypeName());
                bondTypeMap.get(item.getBondTypeId()).setChargeItems(new ArrayList<>());
            }
            if (item.getExpenditureId() != null) {
                bondTypeMap.get(item.getBondTypeId()).getChargeItems().add(
                        ChargeItemVO.builder()
                                .expenditureId(item.getExpenditureId())
                                .expenditureName(item.getExpenditureName())
                                .build()
                );
            }
        }
        List<BondTypeWithItemsVO> bondTypes = new ArrayList<>(bondTypeMap.values());

        // 处理预存类型及其收费项目
        Map<String, PrestoreTypeWithItemsVO> prestoreTypeMap = new HashMap<>();
        for (PrestoreTypeWithItemsVO item : prestoreTypesWithItems) {
            if (!prestoreTypeMap.containsKey(item.getPrestoreTypeId())) {
                prestoreTypeMap.put(item.getPrestoreTypeId(), new PrestoreTypeWithItemsVO());
                prestoreTypeMap.get(item.getPrestoreTypeId()).setPrestoreTypeId(item.getPrestoreTypeId());
                prestoreTypeMap.get(item.getPrestoreTypeId()).setPrestoreTypeName(item.getPrestoreTypeName());
                prestoreTypeMap.get(item.getPrestoreTypeId()).setChargeItems(new ArrayList<>());
            }
            if (item.getExpenditureId() != null) {
                prestoreTypeMap.get(item.getPrestoreTypeId()).getChargeItems().add(
                        ChargeItemVO.builder()
                                .expenditureId(item.getExpenditureId())
                                .expenditureName(item.getExpenditureName())
                                .build()
                );
            }
        }
        List<PrestoreTypeWithItemsVO> prestoreTypes = new ArrayList<>(prestoreTypeMap.values());

        return AllChargeItemsResponseVO.builder()
                .tempChargeItems(tempChargeItems)
                .bondTypes(bondTypes)
                .prestoreTypes(prestoreTypes)
                .build();
    }

    @Override
    public InvoiceInfoResponseVO getInvoiceInfoByCustomerId(Long customerId) {
        // 获取客户开票配置
        CustomerBillingConfigQueryVO billingConfig = getCustomerBillingConfig(customerId);

        // 根据开票方式决定是否返回发票类型和发票抬头列表
        InvoiceInfoResponseVO response = new InvoiceInfoResponseVO();
        response.setCustomerId(customerId);
        response.setBillingMethod(billingConfig.getBillingMethod());

        if ("先款后票".equals(billingConfig.getBillingMethod())) {
            response.setInvoiceType(convertBillingTypeToInvoiceType(billingConfig.getBillingType()));
            response.setHeaders(iCustomerBillingHeaderService.getHeadersByCustomerId(customerId));
        }

        return response;
    }

}
